//
//  DataManager.m
//  Seizure
//
//  Created by student on 2/8/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "DataManager.h"
#import "NotificationSender.h"
#import "Account.h"

@implementation DataManager

static DataManager *sharedInstance = nil;
const double DEFAULT_SENSITIVITY = 50.0;

@synthesize user;
@synthesize local;
@synthesize dbcenter;

// Get the shared instance and create it if necessary
+ (DataManager *)sharedInstance {
    static dispatch_once_t pred;

    if (sharedInstance == nil) {
        // dispatch once, supposedly thread safe initlization of this singleton
        dispatch_once(&pred, ^{
            sharedInstance = [[super alloc] init];
        });
    }
    return sharedInstance;
}

// overrides init (constructor)
// maybe we should get user (from local data) on initalization
- (id)init {
    if (self = [super init]){
        user = nil;
        local = [[LocalData alloc]init];
        dbcenter = [[DBCommunicator alloc]init];
    }
    return self;
}

/**
 * Attempt to create a new account
 */
- (void)createAccountWithEmail:(NSString*)email withName:(NSString*)name withPassword:(NSString*)password isPatient:(Boolean)isPatient 
{
    user = [[Account alloc] initWithName:name email:email isPatient:isPatient apnsKey:@"" sensitivity:[[NSNumber alloc] initWithDouble:DEFAULT_SENSITIVITY]];

}

// Store account to database
-(void)uploadUserWithPassword:(NSString*) password delegate:(id<NetworkData>)delegate requestType:(short)rtype{
    [dbcenter registerUser:user password:password delegate:delegate requestType:rtype];
}

// Used when loading from scratch 
-(Account*) getStoredAccount {
    user = [local retrieveAccount];
    return user;
}

// When user creates account or sign ins
-(void) storeAccount:(Account*)account {
    [local storeAccount:account];
    user=account;
}

/**
 * Parameters: requestingAccount - The account of the current user.
 *              email - The email the user is trying to create a connection to.
 * This method is used by Patients requesting Caretakers and vice versa.
 */
-(NSString*)createPatientCaretakerConnection:(Account *)requestingAccount emailOfRequested:(NSString *)email
{
    if ([self usingFakeAccount])
        return nil;
    else
        return [dbcenter createPatientCaretakerConnection:requestingAccount emailOfRequested:email];
}

/**
 * Parameters: initiatingAccount - The account of the current user.
 *              email - The email the user is trying to break a connection with.
 * This method is used by Patients disconnecting from Caretakers and vice versa.
 */
-(BOOL)removePatientCaretakerConnection:(Account *)initiatingAccount emailToDisconnect:(NSString *)email
{
    return [dbcenter removePatientCaretakerConnection:initiatingAccount emailToDisconnect:email];
}

/**
 * Parameters: caretakersEmail - The email of the caretaker is all that is needed because this patient and caretaker
                                combination has already been set up. 
 * This method is used by Patients confirming a Caretaker and vice versa.
 */
- (BOOL)confirmPatientCaretakerConnection:(NSString *)caretakersEmail
{
    return [dbcenter confirmPatientCaretakerConnection:caretakersEmail];
}

/**
 *  Parameters: patient's email - The email of the patient whose sensitivity has changed.
 *              sensitivity - The value to change the sensitivity to. Valid numbers range from 0 - 100 inclusive.
 *  This method can be used by either a patient or a caretaker
 */
-(BOOL)updatePatientSensitivity:(NSString *)patientEmail sensitivity:(NSNumber *)sensitivity
{
    if ([self usingFakeAccount]){
        return NO;
    }
    else {
        Account *a = [local retrieveAccount];
        a.sensitivity = sensitivity;
        [local storeAccount:a];
    
        return [dbcenter updatePatientSensitivity:patientEmail sensitivity:sensitivity];
    }
}

/**
 * Returns the current sensitivity of this patient.
 */
- (NSNumber*)getPatientSensitivity:(NSString *)patientEmail
{
    if ([self usingFakeAccount])
        return nil;
    else
        return [dbcenter getPatientSensitivity:patientEmail];
}
/**
 * Query database and if valid, log user in
 */
- (void)loginUser:(NSString*)email password:(NSString*)password delegate:(id<NetworkData>)delegate requestType:(short)rtype{
    [dbcenter loginUser:email password:password delegate:delegate requestType:rtype];
}

- (void)userLoggedOut
{
    [local userLoggedOut];
}


- (LinkedAccount *) fetchPatient
{
    return [dbcenter fetchPatient:[user email]];
}


- (NSArray*) fetchCaretakers:(id <NetworkData>)delegate requestType:(short)rtype
{
    if ([self usingFakeAccount])
        return nil;
    else
        return [dbcenter fetchCaretakers:[user email] target:delegate requestType:rtype];
}

// upload the apns key for the user
- (void) uploadAPNSKey:(NSString*)key;
{
    // retrieve key from device
    [user setApnsKey:key];
    [dbcenter uploadAPNSKey:key toUser:[user email]];
}

/**
 *  Add a patient's event log to the database. 
 *  Only Patients may use this.
 */
- (void)addPatientEventLog:(EventLog *)eventLog
{
    NSNumber *latitude = [[NSNumber alloc] initWithDouble:eventLog.locationOfEvent.coordinate.latitude];
    NSNumber *longitude = [[NSNumber alloc] initWithDouble:eventLog.locationOfEvent.coordinate.longitude];
    NSNumber *accuracy = [[NSNumber alloc] initWithDouble:eventLog.locationOfEvent.horizontalAccuracy];
    NSString *patientEmail = [local retrieveAccount].email;
    
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
    dateFormatter.locale = usLocale;
    dateFormatter.dateStyle = NSDateFormatterLongStyle;
    dateFormatter.timeStyle = NSDateFormatterLongStyle;
    
    NSDate *start = eventLog.startTime;
    NSDate *end = eventLog.endTime;
    NSString *startString = [dateFormatter stringFromDate:start];
    NSString *endString = [dateFormatter stringFromDate:end];
    
    //Add Patient Log to local data in case the database upload fails.
    [local addEventLog:eventLog];
    //Add Patient Log to the database (only if they aren't a fake account)
    if (![self usingFakeAccount])
        [dbcenter addPatientEventLog:startString withEndTime:endString withLatitude:latitude withLongitude:longitude withAccuracy:accuracy forPatient:patientEmail];
}

// used for submitting local logs that were not able to be previously uploaded
- (void)addPatientEventLogWithoutLocal:(EventLog *)eventLog
{
    if ([self usingFakeAccount])
        return;
    
    NSNumber *latitude = [[NSNumber alloc] initWithDouble:eventLog.locationOfEvent.coordinate.latitude];
    NSNumber *longitude = [[NSNumber alloc] initWithDouble:eventLog.locationOfEvent.coordinate.longitude];
    NSNumber *accuracy = [[NSNumber alloc] initWithDouble:eventLog.locationOfEvent.horizontalAccuracy];
    NSString *patientEmail = [local retrieveAccount].email;
    
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
    dateFormatter.locale = usLocale;
    dateFormatter.dateStyle = NSDateFormatterLongStyle;
    dateFormatter.timeStyle = NSDateFormatterLongStyle;
    
    NSDate *start = eventLog.startTime;
    NSDate *end = eventLog.endTime;
    NSString *startString = [dateFormatter stringFromDate:start];
    NSString *endString = [dateFormatter stringFromDate:end];
    
    //Add Patient Log to the database
    [dbcenter addPatientEventLog:startString withEndTime:endString withLatitude:latitude withLongitude:longitude withAccuracy:accuracy forPatient:patientEmail];
}

/**
 *  Get all of the logs needed for this account. PHP file will return XML of each log which will be interpreted
 *  into EventLog objects.
 *  
 *  Params: None. Data needed will be accessed through local data (accessing email, account type)
 *  Usage:  Caretakers may get the logs of a Patient that has a confirmed connection with them.
 *          Patients may only get their logs.
 *  Returns: An array of the EventLog objects that are created from the XML.
 */
- (NSArray*)getPatientEventLogs:(id<NetworkData>)target requestType:(int)rtype
{
    Account *requestingAccount = [local retrieveAccount];
    
    //Assume caretaker first.
    NSString *accountType = @"caretaker";
    if( requestingAccount.isPatient )
            accountType = @"patient";                   //If account is patient, switch accountType to "patient"
    
    return [dbcenter getPatientEventLogs:requestingAccount.email forAccountType:accountType target:target requestType:rtype];
}

/**
 * Saves the current patient Event Logs to User Defaults
 */
- (void)archiveLocalLogs:(NSArray *)eventLogs
{
    [local storeEventLogs:(NSArray *)eventLogs];
}

/**
 *  Retrieves the event logs from local data
 */
- (NSArray*)getLocalLogs
{
    return [local retrieveEventLogs];
}

// send push alert to all caretakers of patient
-(void) pushAlertCaretakersWithLocation:(CLLocation *)location
{
    [dbcenter pushAlertForPatient:[user email] location:location];
}

// create (and store) fake account to be used for seizure detection and local logs only
- (void) createFakeAccount 
{
    [self createAccountWithEmail:@"NULL" withName:@"NULL" withPassword:@"NULL" isPatient:YES];
    [self storeAccount:user];
}
// return YES if using detection only account
- (BOOL) usingFakeAccount{
    return [[user email] isEqualToString:@"NULL"];
}

/** 
 *  Reset password.
 */
- (BOOL)resetPassword:(NSString*)email
{
    return [dbcenter resetPassword:email];
}

@end
